package org.budo.dubbo.protocol.http.authentication;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.budo.dubbo.protocol.http.aop.permission.user.UserPermissionCheck;
import org.budo.dubbo.protocol.http.util.BudoReflectionUtil;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.spring.aop.util.AopUtil;
import org.budo.support.spring.context.aware.BudoApplicationContextAware;
import org.springframework.web.method.HandlerMethod;

import lombok.extern.slf4j.Slf4j;

/**
 * @author limingwei
 */
@Slf4j
public class AuthenticationCheckUtil {
    private static final String SSO_CALLBACK = "com.taojin.account.web.api.config.TaojinAccountSsoCallback";

    private static final Boolean HAS_SSO_CALLBACK = ReflectUtil.hasClass(SSO_CALLBACK);

    /**
     * 缓存
     */
    private static final Map<Method, Boolean> _REQUIRE_AUTHENTICATION_CHECK_CACHE = new ConcurrentHashMap<Method, Boolean>();

    public static Boolean requireAuthenticationCheck_aop(Class<?> interfaceType, Method method) {
        Boolean require = _REQUIRE_AUTHENTICATION_CHECK_CACHE.get(method);
        if (null != require) {
            return require;
        }

        require = requireAuthenticationCheck_aop_0(interfaceType, method);
        _REQUIRE_AUTHENTICATION_CHECK_CACHE.put(method, require);
        return require;
    }

    private static Boolean requireAuthenticationCheck_aop_0(Class<?> interfaceType, Method method) {
        if (AopUtil.aopShouldSkip(method)) {
            return false;
        }

        if (HAS_SSO_CALLBACK) {
            Class<?> ssoCallbackType = ReflectUtil.classForName(SSO_CALLBACK);
            if (ssoCallbackType.isAssignableFrom(interfaceType)) {
                return false; // 不需要token认证
            }
        }

        AuthenticationCheck methodAnnotation = BudoReflectionUtil.getMethodAnnotationCached_2(interfaceType, //
                method.getName(), //
                method.getParameterTypes(), //
                AuthenticationCheck.class);

        if (null != methodAnnotation) {
            Boolean require = methodAnnotation.requires() && methodAnnotation.value();

            log.info("#61 require=" + require + ", methodAnnotation=" + methodAnnotation + ", method=" + method);
            return require;
        }

        AuthenticationCheck typeAnnotation = interfaceType.getAnnotation(AuthenticationCheck.class);
        if (null != typeAnnotation) {
            Boolean require = typeAnnotation.requires() && typeAnnotation.value();

            log.info("#69 require=" + require + ", methodAnnotation=" + methodAnnotation + ", method=" + method);
            return require;
        }

        Boolean require = defaultAuthenticationCheck_aop();

        log.info("#75 require=" + require + ", methodAnnotation=" + methodAnnotation + ", method=" + method);
        return require;
    }

    /**
     * 判断是否需要检查
     */
    public static Boolean requireAuthenticationCheck_mvc(HandlerMethod handlerMethod) {
        AuthenticationCheck methodAnnotation = handlerMethod.getMethodAnnotation(AuthenticationCheck.class);
        if (null != methodAnnotation) {
            return methodAnnotation.requires() && methodAnnotation.value();
        }

        AuthenticationCheck typeAnnotation = handlerMethod.getBeanType().getAnnotation(AuthenticationCheck.class);
        if (null != typeAnnotation) {
            return typeAnnotation.requires() && typeAnnotation.value();
        }

        // 没有添加 @AuthenticationCheck 注解时
        if (null != handlerMethod.getMethodAnnotation(UserPermissionCheck.class)) {
            return true;
        }

        return null;
    }

    private static Boolean defaultAuthenticationCheck_aop() {
        if (null == BudoApplicationContextAware.getInstance()) {
            log.debug("#69 defaultAuthenticationCheck_aop return false, not inited");
            return false; // 启动阶段不执行 aopInterceptor
        }

        AuthenticationCheckMethodInterceptor methodInterceptor = BudoApplicationContextAware.getBeanCached_2(AuthenticationCheckMethodInterceptor.class);
        if (null != methodInterceptor) {
            return methodInterceptor.getDefaultAuthenticationCheck();
        }

        return false;
    }
}